import os
from pathlib import Path
import tkinter as tk
import tkinter.messagebox as tkm
import tkinter.ttk as ttk
from rdb import RDB, ModelDB
from rproc import RController
import shutil
from rtreeview import CheckboxTreeview as ctv
from rftp import RFtp


def warn(msg: str):
    tkm.showwarning(title='Warning', message=msg)


def err(msg: str):
    tkm.showerror(title='Error', message=msg)


def adjust_top_win(top_win: tk.Toplevel):
    top_win.update()
    size_str = '%sx%s' % (top_win.winfo_width(), top_win.winfo_height())
    top_win.geometry(size_str + '+400+150')


class RelUi:
    def __init__(self, db: RDB = None):
        self._tk = tk.Tk()
        self._db = db
        self._db.init_entity(sync_all=True)
        self._root_frame = None
        self._init_window()
        self._tk.mainloop()
        self._progress_top_win = None
        self._bar = None
        self._controller = None
        self._thread = None
        self._pop_window = None

    def _pop_progress_bar(self, title='Wait...'):
        """ I want to create a model progress bar dial but it does not work.
            The grab_set seems not work well, may be due to python version?
            May be due to ubuntu os? """
        self._progress_top_win = tk.Toplevel(master=self._tk)
        self._progress_top_win.title(title)
        self._progress_top_win.transient(self._tk)
        self._progress_top_win.grab_set()
        self._bar = ttk.Progressbar(self._progress_top_win, length=200, value=0, mode='indeterminate')
        self._bar.pack()
        adjust_top_win(self._progress_top_win)
        self._bar.start(interval=10)

    def _stop_progress_bar(self):
        self._bar.stop()
        self._bar.destroy()
        self._progress_top_win.destroy()

    def _on_validate(self, d, i, P, s, S, v, V, W):
        self.release_to.delete('0', 'end')
        self.release_to.insert('end', '%s/Firmwares' % P)
        return True

    def _adjust_window(self):
        self._tk.update()
        size_str = '%sx%s' % (self._tk.winfo_width(), self._tk.winfo_height())
        self._tk.geometry(size_str + '+500+400')

    def _init_window(self):
        self._root_frame = tk.Frame(self._tk)
        self._root_frame.pack()
        ttk.Label(self._root_frame, text='Release directory:').grid(row=0, column=0)

        # valid percent substitutiopt=ons (from the Tk entry man page)
        # %P = value of the entry if the edit is allowed
        vcmd = (self._root_frame.register(self._on_validate),
                '%d', '%i', '%P', '%s', '%S', '%v', '%V', '%W')

        entry = ttk.Entry(self._root_frame, validate="key", validatecommand=vcmd,
                          textvariable=self._db.var_release_dir())
        entry.grid(row=0, column=1)

        # release settings
        release_setting = ttk.LabelFrame(self._root_frame, text='Release settings')
        release_setting.grid(row=1, column=0, padx=5, pady=5, columnspan=2)
        ttk.Label(release_setting, text='Release By:').grid(row=0, column=0, sticky='E')
        release_by = ttk.Entry(release_setting, textvariable=self._db.var_release_by())
        release_by.grid(row=0, column=1)

        ttk.Label(release_setting, text='Release To:').grid(row=1, column=0, sticky='E')
        self.release_to = ttk.Entry(release_setting, textvariable=self._db.var_release_to_dir())
        self.release_to.grid(row=1, column=1)

        ttk.Label(release_setting, text='Ftp Host:').grid(row=2, column=0, sticky='E')
        ftp_host = ttk.Entry(release_setting, textvariable=self._db.var_ftp_host())
        ftp_host.grid(row=2, column=1)

        ttk.Label(release_setting, text='Ftp Username:').grid(row=3, column=0, sticky='E')
        ftp_username = ttk.Entry(release_setting, textvariable=self._db.var_ftp_user())
        ftp_username.grid(row=3, column=1)

        ttk.Label(release_setting, text='Ftp Password:').grid(row=4, column=0, sticky='E')
        ftp_password = ttk.Entry(release_setting, textvariable=self._db.var_ftp_passwd())
        ftp_password.grid(row=4, column=1)

        ttk.Button(self._root_frame, text='OK', command=
        self._confirmed).grid(row=2, column=0, columnspan=2, sticky='WE')
        self._adjust_window()

    def _confirmed(self):
        """Got release directory"""
        release_dir = self._db.get_release_dir(cur_var=True)
        if release_dir is None or release_dir == '':
            warn('Please input release directory!')
            return
        release_dir = os.path.expanduser(self._db.get_release_dir(cur_var=True))
        if Path(release_dir).exists() is False:
            warn(str(release_dir) + ' not exists!')
            return
        self._db.set_release_dir(os.path.expanduser(release_dir))

        release_by = self._db.get_release_by(cur_var=True)
        if release_by == '':
            warn('Release By can not be empty')
            return
        self._db.set_release_by(release_by)

        release_to_dir = self._db.get_release_to_dir(cur_var=True)
        if release_to_dir == '':
            warn('Release To can not be empty')
            return
        release_to_dir = os.path.expanduser(release_to_dir)
        if Path(release_to_dir).exists() is False:
            warn('Release To directory is not exists')
            return
        self._db.set_release_to_dir(Path(os.path.expanduser(release_to_dir)).__str__())

        ftp_host = self._db.get_ftp_host(cur_var=True)
        if ftp_host == '':
            warn('Ftp Host can not be empty')
            return
        self._db.set_ftp_host(ftp_host)

        ftp_user = self._db.get_ftp_user(cur_var=True)
        if ftp_user == '':
            warn('Ftp Username can not be empty')
            return
        self._db.set_ftp_user(ftp_user)

        ftp_passwd = self._db.get_ftp_passwd(cur_var=True)
        if ftp_passwd == '':
            warn('Ftp Password can not be empty')
            return
        self._db.set_ftp_passwd(ftp_passwd)

        self._pop_progress_bar()
        self._controller = RController(self._db)
        self._controller.init_param()
        # self._thread = thr.Thread(target=self._controller.init_param())
        # self._thread.start()
        # self._thread.join()
        self._stop_progress_bar()
        self._init_release()

    def _init_release(self):
        '''Create main window to collect release parameters'''
        # destroy root first
        # if self._tk:
        #     self._tk.destroy()

        # self._tk = tk.Tk()
        for c in list(self._tk.children.values()):
            c.destroy()
        self._tk.title('Release')
        model_select = ttk.LabelFrame(self._tk, text='Select models to release')
        model_select.grid(row=0, column=0, padx=5, pady=5)
        # create widgets
        # get model directories array from release controller
        rowidx = 0
        for m in self._db.models():
            # create check boxes
            ttk.Checkbutton(model_select, text=m.__str__(), variable=m.bvar_need_release()).grid(column=0, row=rowidx, sticky='W')
            ttk.Button(model_select, text='Model Settings',
                       command=lambda x=m: self._init_model(x)
                       ).grid(column=1, row=rowidx, sticky='W')
            rowidx += 1

        bnb = ttk.Button(self._tk, text='Set New Feature And Bug Fixed', command=self._init_nf_bf)
        bnb.grid(row=1, column=0, sticky='WE')
        bst = ttk.Button(self._tk, text='Start', command=self._release)
        bst.grid(row=2, column=0, sticky='E')
        self._tk.update()
        size_str = '%sx%s' % (model_select.winfo_width() + 15, model_select.winfo_height()
                              + bst.winfo_height() + bst.winfo_height() + 15)
        self._tk.geometry(size_str)
        self._adjust_window()
        # self._tk.mainloop()

    def _release(self):
        for m in self._db.models():
            if m.need_release():
                m.init_path()
                m.release_notes()
                mroot = m.__str__()
                release_to = Path(self._db.get_release_to_dir()).joinpath(mroot)
                if not release_to.exists():
                    release_to.mkdir(parents=True)
                mdir = release_to.joinpath(m.ver_tail())
                if mdir.exists():
                    shutil.rmtree(mdir, ignore_errors=True)
                adir = mdir.joinpath('all')
                bdir = mdir.joinpath('backup')
                adir.mkdir(parents=True)
                bdir.mkdir(parents=True)
                m.set_local_fw_dir(str(mdir))
                m.set_local_allhex_dir(str(adir))
                m.set_local_backup_dir(str(bdir))
                shutil.copy(m.ori_path()['hex'], mdir)
                shutil.copy(m.ori_path()['cmds'], mdir)
                shutil.copy(m.ori_path()['rn'], mdir)
                shutil.copy(m.ori_path()['mib'], mdir)
                shutil.copy(m.ori_path()['allhex'], adir)
                shutil.copy(m.ori_path()['iss'], bdir)
                shutil.copy(m.ori_path()['bin'], bdir)
        self._init_upload()

    def _upload(self):
        ftp = RFtp(self._db.get_ftp_host())
        ftp.login(self._db.get_ftp_user(), self._db.get_ftp_passwd())
        roots = self._tv.get_children()
        if roots:
            for r in roots:
                children = self._tv.get_children(r)
                if children:
                    for c in children:
                        tags = self._tv.item(c, 'tags')
                        name = self._tv.item(c, 'text')
                        local = tags[1]
                        remote = tags[2]
                        # uncomment next line to open upload function >_<
                        if 'checked' == tags[0]:
                            print ('Upload ' + str(name) + ' from ' + str(local) + ' to ' + str(remote))
                            ftp.upload_file(local, remote, name)

        ftp.close()

    def _init_upload(self):
        self._c_pop_window = tk.Toplevel(master=self._tk)
        self._c_pop_window.geometry('500x400')
        self._c_pop_window.title('Select files to upload')
        self._c_pop_window.transient(self._tk)
        self._c_pop_window.grab_set()

        self._tv = ctv(self._c_pop_window, show='tree')
        self._tv.pack(expand=tk.YES, fill=tk.BOTH)
        upload_button = ttk.Button(self._c_pop_window, text='Upload', command=self._upload)
        upload_button.pack(expand=tk.YES, fill=tk.BOTH)
        for m in self._db.models():
            if m.need_release():
                self._tv.insert('', 0, m.__str__(), text=m.__str__(), tags=('checked', ))
                self._tv.insert(m.__str__(), 'end', str(m.ori_path()['hex']), text=m.hex(),
                        tags=('checked', m.get_local_fw_dir(), m.get_ftp_fw_dir()))
                self._tv.insert(m.__str__(), 'end', str(m.ori_path()['cmds']), text=m.cmds(),
                          tags=('checked', m.get_local_fw_dir(), m.get_ftp_fw_dir()))
                self._tv.insert(m.__str__(), 'end', str(m.ori_path()['rn']), text=m.release_note(),
                          tags=('checked', m.get_local_fw_dir(), m.get_ftp_fw_dir()))
                self._tv.insert(m.__str__(), 'end', str(m.ori_path()['mib']), text=m.mib(),
                          tags=('checked', m.get_local_fw_dir(), m.get_ftp_fw_dir()))
                self._tv.insert(m.__str__(), 'end', str(m.ori_path()['allhex']), text=m.allhex(),
                          tags=('checked', m.get_local_allhex_dir(), m.get_ftp_allhex_dir()))
                self._tv.insert(m.__str__(), 'end', str(m.ori_path()['iss']), text=m.iss(),
                          tags=('checked', m.get_local_backup_dir(), m.get_ftp_backup_dir()))
                self._tv.insert(m.__str__(), 'end', str(m.ori_path()['bin']), text=m.bin(),
                          tags=('checked', m.get_local_backup_dir(), m.get_ftp_backup_dir()))

    def _init_nf_bf(self):
        self._pop_window = tk.Toplevel(master=self._tk)
        self._pop_window.title('New Feature and Bug Fixed')
        self._pop_window.transient(self._tk)
        self._pop_window.grab_set()

        # new feature and fixed bug
        new_feature = ttk.LabelFrame(self._pop_window, text='New Feature')
        new_feature.grid(row=0, column=0, padx=5, pady=5)
        self.new_feature_text = tk.Text(new_feature, height=8)
        self.new_feature_text.insert('insert', self._db.get_global_new_feature())
        self.new_feature_text.grid(row=0, column=0)

        bug_fixed = ttk.LabelFrame(self._pop_window, text='Bug Fixed')
        bug_fixed.grid(row=1, column=0, padx=5, pady=5)
        self.bug_fixed_text = tk.Text(bug_fixed, height=8)
        self.bug_fixed_text.insert('insert', self._db.get_global_bug_fixed())
        self.bug_fixed_text.grid(row=1, column=0)

        confirm_button = ttk.Button(self._pop_window, text='OK', command=self._nf_bf_confirmed)
        confirm_button.grid(row=2, column=0, sticky='E')

    def _nf_bf_confirmed(self):
        self._db.set_global_new_feature(self.new_feature_text.get('1.0', tk.END), sync_var=True)
        self._db.set_global_bug_fixed(self.bug_fixed_text.get('1.0', tk.END), sync_var=True)
        self._db.sync_global_new_feature()
        self._db.sync_global_bug_fixed()
        self._pop_window.destroy()

    def _init_model(self, m: ModelDB):
        """ All settings for designated model """
        self._pop_window = tk.Toplevel(master=self._tk)
        self._pop_window.title(m.__str__())
        self._pop_window.transient(self._tk)
        self._pop_window.grab_set()
        # branches
        branch_setting = ttk.LabelFrame(self._pop_window, text='Branch Settings')
        branch_setting.grid(row=0, column=0, padx=2, pady=5, sticky='NSEW')
        row_idx = 0
        for repo in m.repos():
            ttk.Label(branch_setting, text=
            '/'.join(repo.split('/')[-4:-2])).grid(
                row=row_idx, column=0, sticky='W', padx=5)
            self._br_box = ttk.Combobox(
                branch_setting, state='readonly', width=35,
                textvariable=m.var_repo_br(repo))
            self._br_box.grid(row=row_idx, column=1, sticky='WNS')
            self._br_box['values'] = m.get_all_branches(repo)
            self._br_sync = ttk.Button(branch_setting, text='Sync',
                                       command=lambda r=repo: m.sync_repo_br(r))
            self._br_sync.grid(row=row_idx, column=2, sticky='W')
            row_idx += 1

        self._all_br_sync = ttk.Button(branch_setting, text='Sync', command=lambda: m.sync_repo_br_all())
        self._all_br_sync.grid(row=0, column=3, rowspan=6, sticky='WENS')
        ftp_setting = ttk.LabelFrame(self._pop_window, text='Ftp Settings')
        ftp_setting.grid(row=1, column=0, padx=5, pady=5, columnspan=1, sticky='WE')

        ttk.Label(ftp_setting, text='Firmware Directory: ').grid(row=0, column=0, sticky='E')
        ttk.Label(ftp_setting, text='Backup Directory: ').grid(row=1, column=0, sticky='E')
        ttk.Label(ftp_setting, text='All Hex Directory: ').grid(row=2, column=0, sticky='E')
        ttk.Label(ftp_setting, text='Last Release Note: ').grid(row=3, column=0, sticky='E')
        ttk.Button(ftp_setting, text='AutoDetect', width=11,
                   command=m.auto_detect_ftp_path).grid(row=0, column=2, rowspan=5, sticky='NS')
        ttk.Checkbutton(ftp_setting, text='Use local release note path',
                        variable=m.bvar_releasenote_local()).grid(row=4, column=1, sticky='E')
        tk.Entry(ftp_setting, textvariable=m.var_ftp_fw_dir(), width=55).grid(row=0, column=1, sticky='WE')
        tk.Entry(ftp_setting, textvariable=m.var_ftp_allhex_dir(), width=55).grid(row=1, column=1, sticky='WE')
        tk.Entry(ftp_setting, textvariable=m.var_ftp_backup_dir(), width=55).grid(row=2, column=1, sticky='WE')
        tk.Entry(ftp_setting, textvariable=m.var_last_releasenote_dir(), width=55).grid(row=3, column=1, sticky='WE')

        # new feature and fixed bug
        new_feature = ttk.LabelFrame(self._pop_window, text='New Feature')
        new_feature.grid(row=2, column=0, padx=5, pady=5, columnspan=1, sticky='WE')
        new_feature_text = tk.Text(new_feature, height=8)
        new_feature_text.insert('insert', m.get_new_feature())
        new_feature_text.pack(fill='both')
        m.set_tk_text_nf(new_feature_text)

        bug_fixed = ttk.LabelFrame(self._pop_window, text='Bug Fixed')
        bug_fixed.grid(row=3, column=0, padx=5, pady=5, columnspan=1, sticky='WE')
        bug_fixed_text = tk.Text(bug_fixed, height=8)
        bug_fixed_text.insert('insert', m.get_bug_fixed())
        bug_fixed_text.pack(fill='both')
        m.set_tk_text_bf(bug_fixed_text)

        ttk.Button(self._pop_window, text='OK', command=
            lambda x=m: self._model_settings_confirmed(m)).grid(row=4, column=0, sticky='E')

    def _model_settings_confirmed(self, m: ModelDB):
        ftp_fw_dir = m.get_ftp_fw_dir(cur_var=True)
        ftp_backup_dir = m.get_ftp_backup_dir(cur_var=True)
        ftp_allhex_dir = m.get_ftp_allhex_dir(cur_var=True)
        last_releasenote_dir = m.get_last_releasenote_dir(cur_var=True)
        if ftp_fw_dir == "" or ftp_fw_dir.isspace():
            warn("Ftp frimware directory is empty!")
            return
        if ftp_backup_dir == "" or ftp_backup_dir.isspace():
            warn("Ftp backup directory is empty!")
            return
        if ftp_allhex_dir == "" or ftp_allhex_dir.isspace():
            warn("Ftp all hex directory is empty!")
            return
        if last_releasenote_dir == "" or ftp_allhex_dir.isspace():
            warn("Last release note directory is empty!")
            return
        m.set_new_feature(m.get_tk_text_nf().get('1.0', tk.END))
        m.set_bug_fixed(m.get_tk_text_bf().get('1.0', tk.END))
        m.sync_db()
        self._pop_window.destroy()


def init_db(db: RDB):
    db.set_ftp_host('172.22.102.202')
    db.set_ftp_user('wangqiang')
    db.set_ftp_passwd('wangqiang')


if __name__ == '__main__':
    release_db = RDB()
    init_db(release_db)
    controller = RelUi(db=release_db)
